前幾天都是畫面很簡單的範例,從今天起就進入App UI設計的部分
大概會有五篇範例,都照著練(打)完,會有基本的UI 設計能力
寫寫一些APP的介面不算難事 :)
app.js
import * as React from 'react';
import { View, Text } from 'react-native';
import { StatusBar } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import COLORS from "./src/consts/Colors";
import HomeScreen from "./src/view/screens/HomeScreen"
import DetailsScreen from "./src/view/screens/DetailsScreen"
const Stack = createNativeStackNavigator();
function App() {
return (
<NavigationContainer>
<StatusBar barStyle="dark-content" backgroundColor={COLORS.green} />
<Stack.Navigator screenOptions={{ header: () => null }}>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App
src/view/screens/DetailsScreen.js
import { StyleSheet, Text, View, Image } from 'react-native';
import React from 'react';
import { SafeAreaView } from 'react-native-safe-area-context';
import Icon from 'react-native-vector-icons/MaterialIcons';
import COLORS from '../../consts/Colors';
const DetailsScreen = ({ navigation, route }) => {
const plant = route.params
// console.log(plant)
return (
<SafeAreaView style={{ flex: 1, backgroundColor: COLORS.white }}>
<View style={styles.Header}>
{/* 回上一頁的箭頭 圖示 */}
<Icon name="arrow-back" size={28} onPress={() => navigation.goBack()} />
<Icon name="shopping-cart" size={28} />
</View>
{/* 商品圖 */}
<View style={styles.ImageContainer}>
<Image
source={plant.img}
style={{ flex: 1, resizeMode: 'contain' }}
/>
</View>
{/* 商品簡介 */}
<View style={styles.DetailsContainer}>
<View style={{
marginLeft: 20,
flexDirection: "row",
alignItems: "flex-end"
}}>
<View style={styles.Line} />
<Text style={{ fontSize: 18, fontWeight: "bold" }}>Best choice</Text>
</View>
{/* 商品名稱 */}
<View style={{
marginLeft: 20,
marginTop: 20,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center"
}}>
<Text style={{ fontSize: 22, fontWeight: "bold" }}>{plant.name}</Text>
{/* 價錢的標籤區塊 */}
<View style={styles.PriceTag}>
{/* 價錢 text */}
<Text style={{
marginLeft: 15,
fontSize: 16,
fontWeight: "bold",
color: COLORS.white
}}>${plant.price}
</Text>
</View>
</View>
{/* 商品簡介 區塊 */}
<View style={{
paddingHorizontal: 20,
marginTop: 10
}}>
<Text style={{ fontSize: 20, fontWeight: "bold" }}>About</Text>
{/* 商品介紹文字 */}
<Text style={{ color: COLORS.grey, fontSize: 16, lineHeight: 22, marginTop: 10 }}>{plant.about}</Text>
{/* 商品訂購 區塊 */}
<View style={{ marginTop: 20, flexDirection: "row", justifyContent: "space-between" }}>
<View style={{ flexDirection: "row", alignItems: "center" }}>
<View style={styles.BorderBtn}>
<Text style={styles.BorderBtnText}>-</Text>
</View>
<Text style={{ marginHorizontal: 10, fontSize: 20, fontWeight: "bold" }}>1</Text>
<View style={styles.BorderBtn}>
<Text style={styles.BorderBtnText}>+</Text>
</View>
</View>
<View style={styles.BuyBtn}>
<Text style={{ color: COLORS.white, fontSize: 18, fontWeight: "bold" }}>Buy</Text>
</View>
</View>
</View>
</View>
</SafeAreaView >
);
};
export default DetailsScreen;
const styles = StyleSheet.create({
Header: {
paddingHorizontal: 20,
marginTop: 20,
flexDirection: "row",
justifyContent: "space-between",
},
ImageContainer: {
flex: 0.3,
marginTop: 20,
justifyContent: "center",
alignItems: "center",
},
DetailsContainer: {
flex: 0.7,
backgroundColor: COLORS.light,
marginHorizontal: 7,
marginBottom: 7,
borderRadius: 20,
marginTop: 30,
paddingTop: 30,
},
Line: {
width: 25,
height: 2,
backgroundColor: COLORS.dark,
marginBottom: 5,
marginRight: 3,
},
PriceTag: {
backgroundColor: COLORS.green,
width: 80,
height: 40,
borderTopLeftRadius: 20,
borderBottomLeftRadius: 20,
justifyContent: "center",
},
BorderBtn: {
borderColor: COLORS.grey,
borderWidth: 1,
borderRadius: 5,
width: 60,
height: 40,
justifyContent: "center",
alignItems: "center"
},
BorderBtnText: {
fontSize: 28,
fontWeight: "bold",
},
BuyBtn: {
width: 100,
height: 40,
backgroundColor: COLORS.green,
justifyContent: "center",
alignItems: "center",
borderRadius: 20,
},
});
src/view/screens/HomeScreen.js
import { StyleSheet, Text, View, TouchableOpacity, FlatList, Dimensions, Image } from 'react-native';
import React, { useState } from 'react';
import { SafeAreaView } from 'react-native-safe-area-context';
import COLORS from '../../consts/Colors';
import Icon from 'react-native-vector-icons/MaterialIcons';
import { TextInput } from 'react-native-gesture-handler';
// 商品資料
import plants from '../../consts/Plants';
//商品框 寬度設定
const width = Dimensions.get("screen").width / 2 - 30
const HomeScreen = ({ navigation }) => {
const categories = ['POPULAR', 'ORGANIC', 'INDOORS', 'SYNTHETIC'];
const [categoryIndex, setCategoryIndex] = useState(0)
const CategoryList = () => {
return (
<View style={styles.CategoryContainer}>
{categories.map((item, index) => (
<TouchableOpacity key={index}
activeOpacity={0.8}
onPress={() => setCategoryIndex(index)} >
<Text style={[styles.CategoryText, categoryIndex == index && styles.CategoryIndexSelected]}>{item}</Text>
</TouchableOpacity>
))}
</View>
)
}
// 商品卡片 plant 要用{}
const Card = ({ plant }) => {
return (
<TouchableOpacity onPress={() => navigation.navigate("Details", plant)}>
<View style={styles.Card} >
<View style={{ alignItems: "flex-end" }}>
<View style={{
width: 30,
height: 30,
borderRadius: 15,
alignItems: "center",
justifyContent: "center",
backgroundColor: plant.like
? "rgba(245, 42, 42,0.2)"
: "rgba(0,0,0,0.2) ",
}}>
<Icon name="favorite" size={18} color={plant.like ? COLORS.red : COLORS.dark} />
</View>
</View>
{/* 商品圖 */}
<View style={{ height: 100, alignItems: "center" }}>
<Image
source={plant.img}
style={{ flex: 1, resizeMode: 'contain' }}
/>
</View>
{/* 商品名稱 */}
<Text style={{ fontSize: 16, fontWeight: "bold", marginTop: 10 }}>{plant.name}</Text>
{/* 商品價格 跟 +號 */}
<View style={{ flexDirection: "row", justifyContent: "space-between", marginTop: 5 }}>
<Text style={{ fontSize: 18, fontWeight: "bold" }} > ${plant.price}</Text>
<View style={{
width: 25,
height: 25,
backgroundColor: COLORS.green,
borderRadius: 5,
justifyContent: "center",
alignItems: "center",
}}>
{/* 影片直接用 text + 看起來沒有置中對齊 改用icon 看起來比較整齊 */}
{/* icon 展示 https://oblador.github.io/react-native-vector-icons/ */}
<Icon name="add" style={{ fontSize: 22, fontWeight: "bold", color: COLORS.white }} />
</View>
</View>
</View >
</TouchableOpacity>
)
}
return (
<SafeAreaView style={{
flex: 1,
paddingHorizontal: 20,
backgroundColor: COLORS.white
}}>
<View style={styles.Header}>
<View>
<Text style={{ fontSize: 25, fontWeight: "bold" }}>Welcome to</Text>
<Text style={{ fontSize: 38, fontWeight: "bold", color: COLORS.green }}>Plant Shop</Text>
</View>
<Icon name="shopping-cart"
size={28}
/>
</View>
<View style={{ marginTop: 30, flexDirection: "row" }}>
<View style={styles.SearchContainer}>
<Icon
name="search"
size={25}
style={{ marginLeft: 20 }}
/>
<TextInput
placeholder='請輸入關鍵字'
style={styles.TextInput}
/>
</View>
<View style={styles.SortBtn}>
<Icon name="sort" size={30} color={COLORS.white} />
</View>
</View>
<CategoryList />
{/* 商品展示區 */}
<FlatList
columnWrapperStyle={{ justifyContent: "space-between" }}
showsVerticalScrollIndicator={false}
contentContainerStyle={{
marginTop: 10,
paddingBottom: 50,
}}
numColumns={2}
data={plants}
renderItem={({ item }) => {
return <Card plant={item} />;
}}
/>
</SafeAreaView>
)
};
export default HomeScreen;
const styles = StyleSheet.create({
Header: {
marginTop: 20,
flexDirection: "row",
justifyContent: "space-between",
},
SearchContainer: {
height: 50,
backgroundColor: COLORS.light,
borderRadius: 10,
// 讓兩個並排的關鍵
flex: 1,
flexDirection: "row",
alignItems: "center",
},
TextInput: {
marginLeft: 10,
fontSize: 18,
fontWeight: "bold",
color: COLORS.dark,
flex: 1,
},
SortBtn: {
marginLeft: 10,
width: 50,
height: 50,
backgroundColor: COLORS.green,
justifyContent: "center",
alignItems: "center",
borderRadius: 10,
},
CategoryContainer: {
flexDirection: "row",
marginTop: 30,
marginBottom: 20,
justifyContent: "space-between",
},
CategoryText: {
fontSize: 16,
fontWeight: "bold",
color: COLORS.grey,
},
CategoryIndexSelected: {
color: COLORS.green,
paddingBottom: 5,
borderBottomWidth: 2,
borderColor: COLORS.green,
},
Card: {
width,
height: 225,
backgroundColor: COLORS.light,
marginHorizontal: 2,
borderRadius: 10,
marginBottom: 20,
padding: 15,
},
});
範例 Source Code:
git clone https://smilehsu@bitbucket.org/smilehsu/yt_example_plantui0128.git